/*
 * AlarmManager.c
 *
 *  Created on: Mar 5, 2012
 *      Author: mikaelgra
 */

#include "AlarmManager.h"
#include "Alarm.h"
#include "ArrayList.h"
#include "gpio/System.h"
#include "gpio/Define.h"

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>


void* AlarmManager_MainLoop(void* args);

/**
 * Function pointer to Alarm triggered callback
 */
void (*AlarmTriggered)(struct stAlarm_t* callback) = NULL;

/**
 * Function pointer to Alarm started callback
 */
void (*AlarmStarted)(struct stAlarm_t* callback) = NULL;

/**
 * ArrayList of all alarms associated with the AlarmManager
 */
struct stArrayList_t *Alarms = NULL;

/**
 * Boolean indicating whether or not the AlarmManager is
 * currently running.
 */
bool_t IsRunning = FALSE;

/**
 * The id of the thread used to monitor the alarms.
 */
pthread_t AlarmManagerThread;

/**
 * Initializes the AlarmManager. This must be called
 * before any other methods.
 */
void AlarmManager_Initialize(void)
{
	Alarms = ArrayList_New(ArrayList_AddressComparison);
	IsRunning = FALSE;
}

/**
 * Finalizes the AlarmManager. This must be the last
 * method called on the AlarmManager.
 */
void AlarmManager_Finalize(void)
{
	// stop if running...
	if(IsRunning)
	{
		AlarmManager_Stop();
	}

	// make sure to reset all used GPIO pins, empty array list and finalize alarms
	while(!ArrayList_IsEmpty(Alarms))
	{
		struct stAlarm_t* alarm = ArrayList_Get(Alarms, 0);
		AlarmManager_RemoveAlarm(alarm);
		Alarm_Finalize(alarm);
	}

	// lastly, free ArrayList
	ArrayList_Finalize(Alarms);
}

/**
 * Starts the AlarmManager. This will internally
 * listen to all alarms, and provide callbacks
 * when an alarm is triggered or started.
 */
void AlarmManager_Start(void)
{
	IsRunning = TRUE;
    pthread_create(&AlarmManagerThread, NULL, AlarmManager_MainLoop, (void*)NULL);
}

/**
 * Stops the AlarmManager. Once this is called
 * callbacks will no longer be provided and
 * alarms will no longer be polled or updated.
 */
void AlarmManager_Stop(void)
{
	IsRunning = FALSE;
	pthread_join(AlarmManagerThread, NULL);
}

/**
 * Adds an Alarm to the AlarmManager.
 *
 * @param alarm
 * 		This is the Alarm to be added
 */
void AlarmManager_AddAlarm(struct stAlarm_t* alarm)
{
	ArrayList_Add(Alarms, alarm);
	System_CreateGpioPin(alarm->gpioPin, defPinInput, defPinLow);
}

/**
 * Removes an Alarm from the AlarmManager.
 *
 * @param alarm
 * 		This is the Alarm to be removed
 */
void AlarmManager_RemoveAlarm(struct stAlarm_t* alarm)
{
	ArrayList_Remove(Alarms, alarm);
	System_RemoveGpioPin(alarm->gpioPin);
}

/**
 * Gets the number of alarms associated with the
 * AlarmManager
 *
 * @return
 * 		The number of alarms
 */
int AlarmManager_GetAlarmCount()
{
	return ArrayList_GetSize(Alarms);
}

/**
 * Gets the Alarm at the specified index.
 *
 * @param index
 * 		Is the index of the alarm to be retrieved
 *
 * @return
 * 		Is the alarm at the index to be retrieved
 */
struct stAlarm_t* AlarmManager_GetAlarmAt(int index)
{
	return (struct stAlarm_t*)ArrayList_Get(Alarms, index);
}

/**
 * Sets the callback handler for when an alarm is triggered
 *
 * @param callback
 * 		This is the callback used when an alarm is triggered
 */
void AlarmManager_SetOnAlarmTriggeredHandler(void (*callback)(struct stAlarm_t* alarm))
{
	AlarmTriggered = callback;
}

/**
 * Sets the callback handler for when an alarm is started
 *
 * @param callback
 * 		This is the callback used when an alarm is started
 */
void AlarmManager_SetOnAlarmStartedHandler(void (*callback)(struct stAlarm_t* alarm))
{
	AlarmStarted = callback;
}

/**
 * This is a method for the poller thread used to monitor
 * all of the alarms.
 *
 * @param args
 * 		Is not used
 *
 * @return
 * 		Is not used
 */
void* AlarmManager_MainLoop(void* args)
{
	while(IsRunning)
	{
		int index;
		for(index = 0; index < ArrayList_GetSize(Alarms) && IsRunning; index++)
		{
			struct stAlarm_t* alarm = ArrayList_Get(Alarms, index);
			int value = System_ReadValue(alarm->gpioPin);
			if(alarm->enAlarmState == eEnabled && value == alarm->triggerValue)
			{
				alarm->enAlarmState = eTriggered;
				if(AlarmTriggered != NULL)
				{
					AlarmTriggered(alarm);
				}
			}
			else if(alarm->enAlarmState == eTriggered)
			{
				alarm->timeSinceTrigger++;
				if(alarm->timeSinceTrigger == alarm->triggerTime)
				{
					alarm->timeSinceTrigger = 0;
					alarm->enAlarmState = eStarted;
					if(AlarmStarted != NULL)
					{
						AlarmStarted(alarm);
					}
				}
			}
		}
		sleep(1);
	}
	return args;
}

